{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "5d019f0b",
   "metadata": {},
   "source": [
    "# 4.4 Open Cascade Technology Geometry (NEW)\n",
    "\n",
    "Netgen provides a Python wrapper around the Open Cascade Technology (OCCT) geometry kernel. It allows to model complex geometric objects. It also allows to import models via STEP format, explore, and modify the geometry. For that geometry type you have to install Netgen with the cmake-flag `-DUSE_OCC=ON`.\n",
    "\n",
    "In the wrapper we aim in bringing most of the structure of OCCT to Python. If you are familiar with the C++ interface of Open Cascade you will recognize many classes. Here you can find the OCCT-Netgen dialect to define the famous [OCCT-bottle](bottle.ipynb) tutorial.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "58fea467",
   "metadata": {},
   "source": [
    "## Construction of 3D objects\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8069ef20",
   "metadata": {},
   "outputs": [],
   "source": [
    "from netgen.occ import *\n",
    "from netgen.webgui import Draw as DrawGeo"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8ed8fdc1",
   "metadata": {},
   "source": [
    "We define a box aligned with the Cartesian coordinates given by two points with minimal and maximal x/y/z coordinates.  A cylinder is given by a point on the axis, a direction vector, the radius $r$, and the height $h$. The symbols `X`, `Y`, and `Z` are predefined basis vectors for the Cartesian coordinates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cb96b076",
   "metadata": {},
   "outputs": [],
   "source": [
    "box = Box(Pnt(0,0,0), Pnt(1,1,1))\n",
    "cyl = Cylinder(Pnt(1,0.5,0.5), X, r=0.3, h=0.5)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b0f6a0f8",
   "metadata": {},
   "source": [
    "The Boolean operations `fuse`, `common`, and `cut` provided by OCCT are made available by the operators `+`, `*` and `-`. More can be found in the [OCCT-documentation](https://dev.opencascade.org/doc/refman/html/class_b_rep_algo_a_p_i___boolean_operation.html). Note that the fuse generates one new solid, there is no interface face where the cylinder is touching the box."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "72d74bfa",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "fused = box+cyl\n",
    "DrawGeo (fused);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d49645c5",
   "metadata": {},
   "source": [
    "The generated objects are Py-wrapped OCCT objects derived from TopoDS_Shape."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "994e60bc",
   "metadata": {},
   "outputs": [],
   "source": [
    "print (\"object type is:\", type(box))\n",
    "print (\"type query tells:\", box.type)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b3c5a289",
   "metadata": {},
   "source": [
    "For mesh generation, we make a Netgen OCC-Geometry from the OCC-shape. Then we can call the Netgen mesh generation as usual:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8d9a6368",
   "metadata": {},
   "outputs": [],
   "source": [
    "from ngsolve import Mesh\n",
    "from ngsolve.webgui import Draw\n",
    "\n",
    "geo = OCCGeometry(fused)\n",
    "mesh = Mesh(geo.GenerateMesh(maxh=0.2))\n",
    "mesh.Curve(3)\n",
    "Draw (mesh, clipping=True);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0df71288",
   "metadata": {},
   "source": [
    "Instead of fusing, we can glue together shapes. Then, the resulting composite solid contains the interface face between the solids. This is important when dealing with separate material regions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "02df609c",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "geo = Glue( [box, cyl])\n",
    "DrawGeo (geo)\n",
    "\n",
    "mesh = Mesh(OCCGeometry(geo).GenerateMesh(maxh=0.2)).Curve(3)\n",
    "Draw (mesh, clipping=True);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4eb7e337",
   "metadata": {},
   "source": [
    "A third option is to form a compound of shapes, then the component shapes are meshed independently:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eb8c682b",
   "metadata": {},
   "outputs": [],
   "source": [
    "geo = Compound( [box, cyl])\n",
    "DrawGeo (geo)\n",
    "\n",
    "mesh = Mesh(OCCGeometry(geo).GenerateMesh(maxh=0.2)).Curve(3)\n",
    "Draw (mesh, clipping=True);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "352f8ee0",
   "metadata": {},
   "source": [
    "## Transformation of shapes\n",
    "\n",
    "We can translate, rotate and mirror shapes. The transformation preserves the original shape, and returns a transformed copy. The translation `Move` takes a vector as argument, the rotation `Rotation` needs an axis given by a point and a direction, and an angle. The sign of the angle reflects the right hand screw rule."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "81c3485e",
   "metadata": {},
   "outputs": [],
   "source": [
    "solid = Box((0,0,0), (5,3,1)) + Sphere((0,0,0), 0.3)\n",
    "solid2 = solid.Move((5,0,2))\n",
    "solid3 = solid.Move((0,0,4)).Rotate( Axis((0,0,4), X), 45)\n",
    "DrawGeo (solid + solid2 + solid3);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ed94373",
   "metadata": {},
   "source": [
    "## Hierarchy of shapes\n",
    "\n",
    "OCCT keeps track of the hierarchy of shapes. A solid knows its faces, a face its edges, and a edge its vertices. We can ask a shape for a list of sub-shapes. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "87a01959",
   "metadata": {},
   "outputs": [],
   "source": [
    "solid = Box((0,0,0), (1,1,1))\n",
    "explodedF = sum( [f.Move(0.2 * (f.center-Pnt(0.5, 0.5, 0.5))) for f in solid.faces] )\n",
    "explodedE = sum( [e.Move(0.2 * (e.center-Pnt(0.5, 0.5, 0.5))) for e in solid.edges] )\n",
    "    \n",
    "DrawGeo ( Compound( [explodedF, explodedE] ) );"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ec56a1fe",
   "metadata": {},
   "outputs": [],
   "source": [
    "for f in solid.faces:\n",
    "    print (\"center of gravity\", f.center)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "16a70f40",
   "metadata": {},
   "outputs": [],
   "source": [
    "e = solid.edges[0]\n",
    "print (\"I am a\", e.type)\n",
    "print (\"with startpoint\", e.start, \"and endpoint\", e.end)   "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a5f31e32",
   "metadata": {},
   "outputs": [],
   "source": [
    "for f in solid.faces:\n",
    "    print (\"face\")\n",
    "    for e in f.edges:\n",
    "        print (\"edge:\", e.start, \"-\", e.end)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f14569bd",
   "metadata": {},
   "source": [
    "## Selection of shapes\n",
    "\n",
    "Sometimes we want to set properties of shapes, boundary conditions or mesh-size. For that we can use shape selectors. \n",
    "\n",
    "* The `Max` and `Min` selectors finds the sub-shapes where the center of gravity has maximal or minimal coordinates in a given direction\n",
    "\n",
    "More of them will come soon ..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0ef0c22e",
   "metadata": {},
   "outputs": [],
   "source": [
    "box = Box((0,0,0), (1,1,1))\n",
    "box.faces.Max(Z).col = (1,0,0)\n",
    "box.faces.Min(Y).col = (0,0,1)\n",
    "DrawGeo (box);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e32b0833",
   "metadata": {},
   "source": [
    "Faces where center of gravity has $x$ coordinate less than 0.8:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ca20e571",
   "metadata": {},
   "outputs": [],
   "source": [
    "DrawGeo (Compound(box.faces[X<0.8]));"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f4b0e296",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
